package com.achao.factory;

import com.achao.annotation.Service;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: achao
 * @DateTime: 2022/7/29 14:10
 * @Description: TODO
 */
public class ServiceFactory {

    public static Map<String, Object> map = new HashMap<>();
    private static List<String> classPaths = new ArrayList<>();

    static {
//        map.put("UserService", new UserServiceImpl());
//        map.put("AdminService", new AdminServiceImpl());
    }

    /**
     * 简单工厂
     *
     * @param key
     * @return
     */
    public static Object getInstance(String key) {
        return map.get(key);
    }

    /**
     * 使用泛型封装简单工厂
     *
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> tClass) {
        String className = tClass.getSimpleName();
        System.out.println("通过反射获取的类名: "+className);
        Object o = map.get(className);
        if (o == null) {
            System.err.println("工厂没有注册该实现类...");
            return null;
        } else {
            return (T) o;
        }
    }


    private static Document document;

    /**
     * 初始化
     */
    static {
        String configName = "src/main/resources/beans.xml";
        parse(configName);
        // 解析bean标签
        parsBeansElement();
        // 解析包标签
        parsPageElement();

        System.out.println("map中的东西: "+map);
    }

    /**
     * 解析xml文件,返回一个Document
     *
     * @param configName
     * @return
     */
    private static Document parse(String configName) {
        SAXReader reader = new SAXReader();
        try {
            document = reader.read(configName);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return document;
    }

    /**
     * 解析<beans></beans>标签
     */
    private static void parsBeansElement() {
        List<Node> nodes = document.selectNodes("//beans/bean");
        // 遍历拿到所有的bean标签,将标签转换成元素,从元素中获取标签值
        System.out.println("nodes集合的长度: " + nodes.size());
        for (Node node : nodes) {
            Element element = (Element) node;
            String id = element.attributeValue("id");
            String className = element.attributeValue("class");
            System.out.println("<bean id = " + id + " class = " + className + ">");
            map.put(id, getInstance(className, false));
        }
    }

    /**
     * 解析<package></package>标签
     */
    private static void parsPageElement() {
        // 1.读取配置文件
        List<Node> nodes = document.selectNodes("//beans/package");
        for (Node node : nodes) {
            Element element = (Element) node;
            // 2.解析,拿到包的路径("com.achao.service")
            String packageName = element.attributeValue("name");
            // 3.路径转换格式("")
            String path = packagePath(packageName);
            System.out.println("io流要的路径: " + path);
            // 4.根据第二第三步获取的路径和包名,拿到["com.achao.service.AdminService",...]
            List<String> classPathFrom = getClassPathFrom(path, packageName);
            for (String s : classPathFrom) {
                System.out.println(s);
                // 5.通过注解和反射,将其一一对应加入map中
                getInstance(s, true);
            }
        }
    }


    /**
     * 获取扫描包的包路径
     * 例: "com.achao.service" -> "src/main/java/com/achao/service"
     *
     * @param packageName
     * @return
     */
    private static String packagePath(String packageName) {
        String path = "src/main/java/" + packageName.replace(".", "/");
        return path;
    }

    /**
     * 获取目录下是java的文件,并将".java"去除
     *
     * @param path
     * @return
     */
    private static List<String> getClassPathFrom(String path, String packageName) {
        File file = new File(path);
        getAllFile(file, packageName);
        System.out.println("这里: ======== "+classPaths);
        return classPaths;
    }

    /**
     * 获取文件下的所有文件
     *
     * @param file
     * @return
     */
    public static void getAllFile(File file, String packageName) {
        File[] listFiles = file.listFiles();
        for (File listFile : listFiles) {
            if (listFile.isDirectory()) {
                String s = packageName +"."+ listFile.getName();
                getAllFile(listFile, s);
            } else {
                String name = listFile.getName();
                int i = name.lastIndexOf(".");
                if (name.substring(i).equals(".java")) {
                    String rs = packageName + "." + name;
                    String classPath = rs.substring(0, rs.length() - 5);
                    classPaths.add(classPath);
                }
            }
        }

    }

    /**
     * 将xml文件中的信息添加到map中
     *
     * @param fullClassName
     * @param flag          [true:使用注解],[false:不使用注解]
     * @return
     */
    public static Object getInstance(String fullClassName, Boolean flag) {
        Class<?> myClass = getMyClass(fullClassName);
        try {
            if (!flag) {
                return myClass.newInstance();
            } else {
                Service annotation = myClass.getDeclaredAnnotation(Service.class);
                if (annotation == null) {

                } else {
                    Object o = myClass.newInstance();
                    String value = annotation.value();
                    if (value != null && !value.equals("")) {
                        map.put(value, o);
                    } else {
                        map.put(myClass.getSimpleName(), o);
                    }
                    Class<?>[] interfaces = myClass.getInterfaces();
                    for (Class<?> anInterface : interfaces) {
                        map.put(anInterface.getSimpleName(), o);
                    }
                }
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static Class<?> getMyClass(String fullClassName) {
        try {
            Class<?> aClass = Class.forName(fullClassName);
            return aClass;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

}
